Ryanair workers in Belgium, Italy, Portugal and Spain went on strike the 25-26th of August, in protest against working conditions and mangements’ unwillingness to engage in collective bargaining. After threats of retaliation against employees that went on strike, Belgian and Swedish trade unions gave an additional stike notification for the 10th of August, with German and Dutch unions potentially joining.
In this quick walkthrough, we crawl recent tweets on this transnational strike using the R-package rtweet, geo-code the location of those tweeting with ggmap and plot those locations on an interactive map using leaflet.
library(rtweet)
library(leaflet)
library(ggmap)
library(ggplot2)
library(dplyr, warn.conflicts = FALSE)
Using the rtweet package and after initialising a Twitter app to get the required token-information, we queried Twitter on 2018-01-01 for hashtags the hashtags “#HuelgaRyanair”, “#Ryanair”, and “#RyanairStrike”.
# create an Twitter token with the proper app-permissions
token = create_token(
app = '<app name>',
consumer_key = 'sLcHGjP...',
consumer_secret = 'eAIYIIaR...',
access_token= "...0TAt7IPe",
access_secret = "...VuGbFKXtcJzrniT",
set_renv = FALSE)
ht_ryanair <- search_tweets(
"#ryanair", n = 18000, include_rts = FALSE,
token = token,
retryonratelimit = TRUE
)
ht_ryanairstrike <- search_tweets(
"#Ryanairstrike", n = 18000, include_rts = TRUE,
token = token,
retryonratelimit = TRUE
)
ht_huelgaRyanair <- search_tweets(
"#HuelgaRyanair", n = 18000, include_rts = FALSE,
token = token,
retryonratelimit = TRUE
)
# Add the querystring / hashtag as a variable
ht_ryanair <- ht_ryanair %>%
mutate(querystring = "#ryanair")
ht_ryanairstrike <- ht_ryanairstrike %>%
mutate(querystring = "#Ryanairstrike")
ht_huelgaRyanair <- ht_huelgaRyanair %>%
mutate(querystring = "#HuelgaRyanair")
tweets <- bind_rows(
ht_huelgaRyanair,
ht_ryanair,
ht_ryanairstrike)
This resulted in 9.043 tweets, taking into account that the non-paying twitter API only allows query go back a week in time. To err on the side of caution and given the behaviour of Ryanair-management, I’m not putting the individual-level crawed data online for this R notebook.
ryanair_tweets <- readRDS('20180801_ryanair_tweets.rds')
dim(ryanair_tweets)
[1] 9043 89
head(ryanair_tweets)
g.timeline <- ggplot(ryanair_tweets, aes(x = created_at, group = querystring, color = querystring))
g.timeline + geom_freqpoly(binwidth = 35000) +
labs(x = NULL, y = NULL, color = 'hashtag',
title = 'Both #ryanair and strike-related hashtags #HuelgaRyanair & #RyanairStrike\npeak during the 25-26th of August strike',
subtitle = 'Number of (re)tweets send, by timestamp and hashtag used')

We can derive the approximate location of the tweets, by geocoding the location-string Twitter-users entered in their public bio.
users_id <- unique(ryanair_tweets$user_id)
ryanair_tweets_userinfo <- lookup_users(users_id, token = token)
ryanair_tweets_userinfo %>%
select(location, followers_count, friends_count, account_lang, description) %>%
slice(1:6)
# only attempt to geocode users with non-empty location strings
user_info_nomiss <- user_info %>%
filter(location != "")
# geocode locations
locations <- ggmap::geocode(user_info_nomiss$location)
# merge geocode location info back in
user_info_nomiss <- bind_rows(
user_info_nomiss,
locations)
ryanair_tweets_geocoded <- left_join(
tweets,
user_info_nomiss %>% select(user_id, lat, lon),
by = 'user_id')
ryanair_tweets_geocoded %>% filter(!is.na(lat)) %>% nrow
[1] 4334
Of the 9.043 tweets, 4.334 where geocoded based on the users’ location-string.
ryanair_tweets_geocoded <- ryanair_tweets_geocoded %>%
select(status_id, text, lat, lon) %>%
filter(!is.na(lat)) %>% # drop tweets in case they have missing coords
filter(status_id != '1022146717855227905') # remove one outlier, Antarctic supporter, for neater map
# get a plane-icon in the markers
icons <- awesomeIcons(
icon = 'fa-plane ',
iconColor = 'black',
library = 'fa'
)
m.tweets <- leaflet(ryanair_tweets_geocoded, width = '100%') %>%
addTiles() %>%
addAwesomeMarkers(
popup = ~text,
lng=~lon,
lat=~lat,
icon=icons,
clusterOptions = markerClusterOptions())
m.tweets %>% setView(lng = 11, lat = 10, zoom = 2)
LS0tCnRpdGxlOiAiTWFwcGluZyB0aGUgI1J5YW5haXJTdHJpa2UgLyAjSHVlbGdhUnlhbmFpciBvbiBUd2l0dGVyIgphdXRob3I6IE1hYXJ0ZW4gSGVybWFucyB8IEBoZXJtYW5zbQpkYXRlOiAyMDE4LTAxLTAxCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KClJ5YW5haXIgd29ya2VycyBpbiBCZWxnaXVtLCBJdGFseSwgUG9ydHVnYWwgYW5kIFNwYWluIHdlbnQgb24gc3RyaWtlIHRoZSAyNS0yNnRoIG9mIEF1Z3VzdCwgaW4gcHJvdGVzdCBhZ2FpbnN0IHdvcmtpbmcgY29uZGl0aW9ucyBhbmQgbWFuZ2VtZW50cycgdW53aWxsaW5nbmVzcyB0byBlbmdhZ2UgaW4gY29sbGVjdGl2ZSBiYXJnYWluaW5nLiBBZnRlciB0aHJlYXRzIG9mIHJldGFsaWF0aW9uIGFnYWluc3QgZW1wbG95ZWVzIHRoYXQgd2VudCBvbiBzdHJpa2UsIEJlbGdpYW4gYW5kIFN3ZWRpc2ggdHJhZGUgdW5pb25zIGdhdmUgYW4gYWRkaXRpb25hbCBzdGlrZSBub3RpZmljYXRpb24gZm9yIHRoZSAxMHRoIG9mIEF1Z3VzdCwgd2l0aCBHZXJtYW4gYW5kIER1dGNoIHVuaW9ucyBwb3RlbnRpYWxseSBqb2luaW5nLgoKSW4gdGhpcyBxdWljayB3YWxrdGhyb3VnaCwgd2UgY3Jhd2wgcmVjZW50IHR3ZWV0cyBvbiB0aGlzIHRyYW5zbmF0aW9uYWwgc3RyaWtlIHVzaW5nIHRoZSBSLXBhY2thZ2UgW3J0d2VldF0oaHR0cHM6Ly9ydHdlZXQuaW5mby8pLCBnZW8tY29kZSB0aGUgbG9jYXRpb24gb2YgdGhvc2UgdHdlZXRpbmcgd2l0aCBbZ2dtYXBdKGh0dHBzOi8vZ2l0aHViLmNvbS9ka2FobGUvZ2dtYXApIGFuZCBwbG90IHRob3NlIGxvY2F0aW9ucyBvbiBhbiBpbnRlcmFjdGl2ZSBtYXAgdXNpbmcgW2xlYWZsZXRdKGh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC8pLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkocnR3ZWV0KQpsaWJyYXJ5KGxlYWZsZXQpCmxpYnJhcnkoZ2dtYXApCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5ciwgd2Fybi5jb25mbGljdHMgPSBGQUxTRSkKYGBgCgpVc2luZyB0aGUgcnR3ZWV0IHBhY2thZ2UgYW5kIGFmdGVyIGluaXRpYWxpc2luZyBhIFR3aXR0ZXIgYXBwIHRvIGdldCB0aGUgcmVxdWlyZWQgdG9rZW4taW5mb3JtYXRpb24sIHdlIHF1ZXJpZWQgVHdpdHRlciBvbiAyMDE4LTAxLTAxIGZvciBoYXNodGFncyB0aGUgaGFzaHRhZ3MgIlsjSHVlbGdhUnlhbmFpcl0oaHR0cHM6Ly90d2l0dGVyLmNvbS9oYXNodGFnL0h1ZWxnYVJ5YW5haXIpIiwgIlsjUnlhbmFpcl0oaHR0cHM6Ly90d2l0dGVyLmNvbS9oYXNodGFnL1J5YW5haXIpIiwgYW5kICJbI1J5YW5haXJTdHJpa2VdKGh0dHBzOi8vdHdpdHRlci5jb20vaGFzaHRhZy9SeWFuYWlyU3RyaWtlKSIuIAoKYGBge3IsIGV2YWw9RkFMU0V9CiMgY3JlYXRlIGFuIFR3aXR0ZXIgdG9rZW4gd2l0aCB0aGUgcHJvcGVyIGFwcC1wZXJtaXNzaW9ucwp0b2tlbiA9IGNyZWF0ZV90b2tlbigKICBhcHAgPSAnPGFwcCBuYW1lPicsCiAgY29uc3VtZXJfa2V5ID0gJ3NMY0hHalAuLi4nLAogIGNvbnN1bWVyX3NlY3JldCA9ICdlQUlZSUlhUi4uLicsCiAgYWNjZXNzX3Rva2VuPSAiLi4uMFRBdDdJUGUiLAogIGFjY2Vzc19zZWNyZXQgPSAiLi4uVnVHYkZLWHRjSnpybmlUIiwKICBzZXRfcmVudiA9IEZBTFNFKQoKaHRfcnlhbmFpciA8LSBzZWFyY2hfdHdlZXRzKAogICIjcnlhbmFpciIsIG4gPSAxODAwMCwgaW5jbHVkZV9ydHMgPSBGQUxTRSwKICB0b2tlbiA9IHRva2VuLAogIHJldHJ5b25yYXRlbGltaXQgPSBUUlVFCikKCmh0X3J5YW5haXJzdHJpa2UgPC0gc2VhcmNoX3R3ZWV0cygKICAiI1J5YW5haXJzdHJpa2UiLCBuID0gMTgwMDAsIGluY2x1ZGVfcnRzID0gVFJVRSwKICB0b2tlbiA9IHRva2VuLAogIHJldHJ5b25yYXRlbGltaXQgPSBUUlVFCikKCmh0X2h1ZWxnYVJ5YW5haXIgPC0gc2VhcmNoX3R3ZWV0cygKICAiI0h1ZWxnYVJ5YW5haXIiLCBuID0gMTgwMDAsIGluY2x1ZGVfcnRzID0gRkFMU0UsCiAgdG9rZW4gPSB0b2tlbiwKICByZXRyeW9ucmF0ZWxpbWl0ID0gVFJVRQopCgojIEFkZCB0aGUgcXVlcnlzdHJpbmcgLyBoYXNodGFnIGFzIGEgdmFyaWFibGUKaHRfcnlhbmFpciA8LSBodF9yeWFuYWlyICU+JQogIG11dGF0ZShxdWVyeXN0cmluZyA9ICIjcnlhbmFpciIpCmh0X3J5YW5haXJzdHJpa2UgPC0gaHRfcnlhbmFpcnN0cmlrZSAlPiUKICBtdXRhdGUocXVlcnlzdHJpbmcgPSAiI1J5YW5haXJzdHJpa2UiKQpodF9odWVsZ2FSeWFuYWlyIDwtIGh0X2h1ZWxnYVJ5YW5haXIgJT4lCiAgbXV0YXRlKHF1ZXJ5c3RyaW5nID0gIiNIdWVsZ2FSeWFuYWlyIikKCnR3ZWV0cyA8LSBiaW5kX3Jvd3MoCiAgaHRfaHVlbGdhUnlhbmFpciwKICBodF9yeWFuYWlyLAogIGh0X3J5YW5haXJzdHJpa2UpCgpgYGAKClRoaXMgcmVzdWx0ZWQgaW4gOS4wNDMgdHdlZXRzLCB0YWtpbmcgaW50byBhY2NvdW50IHRoYXQgdGhlIG5vbi1wYXlpbmcgdHdpdHRlciBBUEkgb25seSBhbGxvd3MgcXVlcnkgZ28gYmFjayBhIHdlZWsgaW4gdGltZS4gVG8gZXJyIG9uIHRoZSBzaWRlIG9mIGNhdXRpb24gYW5kIGdpdmVuIHRoZSBiZWhhdmlvdXIgb2YgUnlhbmFpci1tYW5hZ2VtZW50LCBJJ20gbm90IHB1dHRpbmcgdGhlIGluZGl2aWR1YWwtbGV2ZWwgY3Jhd2VkIGRhdGEgb25saW5lIGZvciB0aGlzIFIgbm90ZWJvb2suCgpgYGB7ciwgZWNobz1GQUxTRX0KbGlicmFyeShhc3NlcnRyKQpyeWFuYWlyX3R3ZWV0cyA8LSByZWFkUkRTKGZpbGUucGF0aChTeXMuZ2V0ZW52KCdEQVRBRElSX0hJVkFfTE9DQUwnKSwgJy90d2l0dGVyLzIwMTgwMTAxX3R3aXR0ZXJfY3Jhd2xfcnlhbmFpci8yMDE4MDgwMV9yeWFuYWlyX3R3ZWV0cy5yZHMnKSkgJT4lCiAgdmVyaWZ5KGRpbSguKSA9PSBjKDkwNDMsIDg5KSkKCnJ5YW5haXJfdHdlZXRzX2dlb2NvZGVkIDwtIHJlYWRSRFMoZmlsZS5wYXRoKFN5cy5nZXRlbnYoJ0RBVEFESVJfSElWQV9MT0NBTCcpLCAnL3R3aXR0ZXIvMjAxODAxMDFfdHdpdHRlcl9jcmF3bF9yeWFuYWlyLzIwMTgwODAxX3J5YW5haXJfdHdlZXRzX2dlb2NvZGVkLnJkcycpKSAlPiUKICB2ZXJpZnkoZGltKC4pID09IGMoOTA0MywgOTEpKQoKcnlhbmFpcl90d2VldHNfdXNlcmluZm8gPC0gcmVhZFJEUyhmaWxlLnBhdGgoU3lzLmdldGVudignREFUQURJUl9ISVZBX0xPQ0FMJyksICcvdHdpdHRlci8yMDE4MDEwMV90d2l0dGVyX2NyYXdsX3J5YW5haXIvMjAxODA4MDFfcnlhbmFpcl91c2Vycy5yZHMnKSkgJT4lCiAgdmVyaWZ5KGRpbSguKSA9PSBjKDQ4NjgsIDg5KSkKCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQpyeWFuYWlyX3R3ZWV0cyA8LSByZWFkUkRTKCcyMDE4MDgwMV9yeWFuYWlyX3R3ZWV0cy5yZHMnKQpgYGAKCmBgYHtyfQpkaW0ocnlhbmFpcl90d2VldHMpCmBgYAoKYGBge3J9CmhlYWQocnlhbmFpcl90d2VldHMpCmBgYAoKCmBgYHtyfQpnLnRpbWVsaW5lIDwtIGdncGxvdChyeWFuYWlyX3R3ZWV0cywgYWVzKHggPSBjcmVhdGVkX2F0LCBncm91cCA9IHF1ZXJ5c3RyaW5nLCBjb2xvciA9IHF1ZXJ5c3RyaW5nKSkKZy50aW1lbGluZSArIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAzNTAwMCkgKyAKICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCwgY29sb3IgPSAnaGFzaHRhZycsCiAgICAgICB0aXRsZSA9ICdCb3RoICNyeWFuYWlyIGFuZCBzdHJpa2UtcmVsYXRlZCBoYXNodGFncyAjSHVlbGdhUnlhbmFpciAmICNSeWFuYWlyU3RyaWtlXG5wZWFrIGR1cmluZyB0aGUgMjUtMjZ0aCBvZiBBdWd1c3Qgc3RyaWtlJywKICAgICAgIHN1YnRpdGxlID0gJ051bWJlciBvZiAocmUpdHdlZXRzIHNlbmQsIGJ5IHRpbWVzdGFtcCBhbmQgaGFzaHRhZyB1c2VkJykKYGBgCgpXZSBjYW4gZGVyaXZlIHRoZSBhcHByb3hpbWF0ZSBsb2NhdGlvbiBvZiB0aGUgdHdlZXRzLCBieSBnZW9jb2RpbmcgdGhlIGxvY2F0aW9uLXN0cmluZyBUd2l0dGVyLXVzZXJzIGVudGVyZWQgaW4gdGhlaXIgcHVibGljIGJpby4KCmBgYHtyLCBldmFsPUZBTFNFfQp1c2Vyc19pZCA8LSB1bmlxdWUocnlhbmFpcl90d2VldHMkdXNlcl9pZCkKcnlhbmFpcl90d2VldHNfdXNlcmluZm8gPC0gbG9va3VwX3VzZXJzKHVzZXJzX2lkLCB0b2tlbiA9IHRva2VuKQoKcnlhbmFpcl90d2VldHNfdXNlcmluZm8gJT4lIAogIHNlbGVjdChsb2NhdGlvbiwgZm9sbG93ZXJzX2NvdW50LCBmcmllbmRzX2NvdW50LCBhY2NvdW50X2xhbmcsIGRlc2NyaXB0aW9uKSAlPiUKICBzbGljZSgxOjYpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CnJ5YW5haXJfdHdlZXRzX3VzZXJpbmZvICU+JSBzbGljZSgxOjYpICU+JSBzZWxlY3QobG9jYXRpb24sIGZvbGxvd2Vyc19jb3VudCwgZnJpZW5kc19jb3VudCwgYWNjb3VudF9sYW5nLCBkZXNjcmlwdGlvbikKYGBgCgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgb25seSBhdHRlbXB0IHRvIGdlb2NvZGUgdXNlcnMgd2l0aCBub24tZW1wdHkgbG9jYXRpb24gc3RyaW5ncwp1c2VyX2luZm9fbm9taXNzIDwtIHVzZXJfaW5mbyAlPiUKICBmaWx0ZXIobG9jYXRpb24gIT0gIiIpCgojIGdlb2NvZGUgbG9jYXRpb25zCmxvY2F0aW9ucyA8LSBnZ21hcDo6Z2VvY29kZSh1c2VyX2luZm9fbm9taXNzJGxvY2F0aW9uKQoKIyBtZXJnZSBnZW9jb2RlIGxvY2F0aW9uIGluZm8gYmFjayBpbiAKdXNlcl9pbmZvX25vbWlzcyA8LSBiaW5kX3Jvd3MoCiAgdXNlcl9pbmZvX25vbWlzcywgCiAgbG9jYXRpb25zKQoKcnlhbmFpcl90d2VldHNfZ2VvY29kZWQgPC0gbGVmdF9qb2luKAogIHR3ZWV0cywgCiAgdXNlcl9pbmZvX25vbWlzcyAlPiUgc2VsZWN0KHVzZXJfaWQsIGxhdCwgbG9uKSwKICBieSA9ICd1c2VyX2lkJykKCmBgYAoKYGBge3J9CnJ5YW5haXJfdHdlZXRzX2dlb2NvZGVkICU+JSBmaWx0ZXIoIWlzLm5hKGxhdCkpICU+JSBucm93CmBgYAoKT2YgdGhlIDkuMDQzIHR3ZWV0cywgNC4zMzQgd2hlcmUgZ2VvY29kZWQgYmFzZWQgb24gdGhlIHVzZXJzJyBsb2NhdGlvbi1zdHJpbmcuCgpgYGB7cn0KcnlhbmFpcl90d2VldHNfZ2VvY29kZWQgPC0gcnlhbmFpcl90d2VldHNfZ2VvY29kZWQgJT4lIAogIHNlbGVjdChzdGF0dXNfaWQsIHRleHQsIGxhdCwgbG9uKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGxhdCkpICU+JSAjIGRyb3AgdHdlZXRzIGluIGNhc2UgdGhleSBoYXZlIG1pc3NpbmcgY29vcmRzCiAgZmlsdGVyKHN0YXR1c19pZCAhPSAnMTAyMjE0NjcxNzg1NTIyNzkwNScpICMgcmVtb3ZlIG9uZSBvdXRsaWVyLCBBbnRhcmN0aWMgc3VwcG9ydGVyLCBmb3IgbmVhdGVyIG1hcApgYGAKCmBgYHtyfQojIGdldCBhIHBsYW5lLWljb24gaW4gdGhlIG1hcmtlcnMKaWNvbnMgPC0gYXdlc29tZUljb25zKAogIGljb24gPSAnZmEtcGxhbmUgJywKICBpY29uQ29sb3IgPSAnYmxhY2snLAogIGxpYnJhcnkgPSAnZmEnCikKCm0udHdlZXRzIDwtIGxlYWZsZXQocnlhbmFpcl90d2VldHNfZ2VvY29kZWQsIHdpZHRoID0gJzEwMCUnKSAlPiUKICBhZGRUaWxlcygpICU+JSAKICBhZGRBd2Vzb21lTWFya2VycygKICAgIHBvcHVwID0gfnRleHQsCiAgICBsbmc9fmxvbiwgCiAgICBsYXQ9fmxhdCwgCiAgICBpY29uPWljb25zLAogICAgY2x1c3Rlck9wdGlvbnMgPSBtYXJrZXJDbHVzdGVyT3B0aW9ucygpKQpgYGAKCmBgYHtyfQptLnR3ZWV0cyAlPiUgc2V0VmlldyhsbmcgPSAxMSwgbGF0ID0gMTAsIHpvb20gPSAyKQpgYGAKCgo=